---
title: File Upload
description: Using the file-upload machine in your project.
package: "@zag-js/file-upload"
---

File upload component is used to upload multiple files.

The native input file element is quite difficult to style and doesn't provide a
drag-n-drop version.

> The file upload component doesn't handle the actual file uploading process. It
> only handles the UI and the state of the file upload.

<Resources pkg="@zag-js/file-upload" />

<Showcase id="FileUpload" />

**Features**

- Supports a button to open the file dialog
- Supports drag and drop to upload files
- Set the maximum number of files that can be uploaded
- Set the maximum size of the files that can be uploaded
- Set the accepted file types

## Installation

To use the file upload machine in your project, run the following command in
your command line:

<CodeSnippet id="file-upload/installation.mdx" />

## Anatomy

To set up the file upload correctly, you'll need to understand its anatomy and
how we name its parts.

> Each part includes a `data-part` attribute to help identify them in the DOM.

<Anatomy id="file-upload" />

## Usage

First, import the file upload package into your project

```jsx
import * as fileUpload from "@zag-js/file-upload"
```

The file upload package exports two key functions:

- `machine` — The state machine logic for the file upload widget.
- `connect` — The function that translates the machine's state to JSX attributes
  and event handlers.

Next, import the required hooks and functions for your framework and use the
file upload machine in your project 🔥

<CodeSnippet id="file-upload/usage.mdx" />

### Setting the accepted file types

Use the `accept` attribute to set the accepted file types.

```jsx
const service = useMachine(fileUpload.machine, {
  accept: "image/*",
})
```

Alternatively, you can provide an object with a MIME type and an array of file
extensions.

```jsx
const service = useMachine(fileUpload.machine, {
  accept: {
    "image/png": [".png"],
    "text/html": [".html", ".htm"],
  },
})
```

### Setting the maximum number of files

Use the `maxFiles` attribute to set the maximum number of files that can be
uploaded. This will set the `multiple` attribute on the underlying input
element.

```jsx
const service = useMachine(fileUpload.machine, {
  maxFiles: 5,
})
```

### Setting the maximum size per file

Use the `maxFileSize` attribute to set the maximum size per file that can be
uploaded.

```jsx
const service = useMachine(fileUpload.machine, {
  maxFileSize: 1024 * 1024 * 10, // 10MB
})
```

### Listening to file changes

When files are uploaded, the `onFileChange` callback is invoked with the details
of the accepted and rejected files.

```jsx
const service = useMachine(fileUpload.machine, {
  onFileChange(details) {
    // details => { acceptedFiles: File[], rejectedFiles: { file: File, errors: [] }[] }
    console.log(details.acceptedFiles)
    console.log(details.rejectedFiles)
  },
})
```

### Usage within a form

To use the file upload within a form, set the `name` attribute in the machine's
context, and ensure you render the input element `api.getHiddenInputProps()`

```jsx
const service = useMachine(fileUpload.machine, {
  name: "avatar",
})
```

### Displaying image preview

To display a preview of the uploaded image, use the built-in FileReader API to
read the file and set the `src` attribute of an image element.

```jsx
const service = useMachine(fileUpload.machine, {
  onFileChange(details) {
    const reader = new FileReader()
    reader.onload = (event) => {
      const image = event.target.result
      // set the image as the src of an image element
    }
    reader.readAsDataURL(details.acceptedFiles[0])
  },
})
```

### Applying custom validation

To apply custom validation, set the `validate` attribute to a function that
returns an **array of error strings**.

The returned array can contain any string as an error message. While zagjs
supports default errors such as `TOO_MANY_FILES`, `FILE_INVALID_TYPE`,
`FILE_TOO_LARGE`, or `FILE_TOO_SMALL`, you can return any string that represents
your custom validation errors.

> Return `null` if no validation errors are detected.

```jsx
const service = useMachine(fileUpload.machine, {
  validate(file) {
    // Check if file size exceeds 10MB
    if (file.size > 1024 * 1024 * 10) {
      return ["FILE_TOO_LARGE"]
    }
    return null
  },
})
```

Apply multiple validation errors:

```js
const service = useMachine(fileUpload.machine, {
  validate(file) {
    const errors = []

    // Check file size
    if (file.size > 10 * 1024 * 1024) {
      errors.push("FILE_TOO_LARGE") // Default error enum
    }

    // Ensure file is a PDF
    if (!file.name.endsWith(".pdf")) {
      errors.push("ONLY_PDF_ALLOWED") // Custom error
    }

    // Custom check: Reject duplicate files
    const isDuplicate = details.acceptedFiles.some(
      (acceptedFile) => acceptedFile.name === file.name,
    )
    if (isDuplicate) {
      errors.push("FILE_EXISTS")
    }

    return errors.length > 0 ? errors : null
  },
})
```

### Disabling drag and drop

To disable the drag and drop functionality, set the `allowDrop` context property
to `false`.

```jsx
const service = useMachine(fileUpload.machine, {
  allowDrop: false,
})
```

### Allowing directory selection

Set the `directory` property to `true` to enable selecting directories instead
of files.

This maps to the native input `webkitdirectory` HTML attribute and allows users
to select directories and their contents.

> Please note that support for this feature varies from browser to browser.

```jsx
const service = useMachine(fileUpload.machine, {
  directory: true,
})
```

### Supporting media capture on mobile devices

Set the `capture` property to specify the media capture mechanism to capture
media on the spot. The value can be:

- `user` for capturing media from the user-facing camera
- `environment` for the outward-facing camera

> This behavior only works on mobile devices. On desktop devices, it will open
> the file system like normal.

```jsx
const service = useMachine(fileUpload.machine, {
  capture: "user",
})
```

### Pasting files from clipboard

After a user copies an image, to allow pasting the files from the clipboard, you
can listen for the paste event and use the `api.setFiles` method to set the
files.

Here's an example of how to do this in React.

```jsx
function Demo() {
  const service = useMachine(fileUpload.machine, {
    accept: "image/*",
  })

  const api = fileUpload.connect(service, normalizeProps)

  return (
    <textarea
      onPaste={(event) => {
        if (event.clipboardData?.files) {
          api.setFiles(Array.from(event.clipboardData.files))
        }
      }}
    />
  )
}
```

### Transforming files before acceptance

Use the `transformFiles` callback to process files before they're added to
`acceptedFiles`. This is useful for scenarios like image cropping, compression,
or format conversion.

The `transformFiles` function receives the selected files and should return a
promise that resolves with the transformed files.

```jsx
const service = useMachine(fileUpload.machine, {
  accept: "image/*",
  transformFiles: async (files) => {
    return Promise.all(
      files.map(async (file) => {
        // Compress or transform the file
        const transformedBlob = await processImage(file)
        return new File([transformedBlob], file.name, { type: file.type })
      }),
    )
  },
})
```

While files are being transformed, the `api.transforming` boolean is `true`,
allowing you to show loading states in your UI.

## Styling guide

Earlier, we mentioned that each file upload part has a `data-part` attribute
added to them to select and style them in the DOM.

```css
[data-part="root"] {
  /* styles for root element*/
}

[data-part="dropzone"] {
  /* styles for root element*/
}

[data-part="trigger"] {
  /* styles for file picker trigger */
}

[data-part="label"] {
  /* styles for the input's label */
}
```

### Dragging State

When the user drags a file over the file upload, the `data-dragging` attribute
is added to the `root` and `dropzone` parts.

```css
[data-part="root"][data-dragging] {
  /* styles for when the user is dragging a file over the file upload */
}

[data-part="dropzone"][data-dragging] {
  /* styles for when the user is dragging a file over the file upload */
}
```

### Disabled State

When the file upload is disabled, the `data-disabled` attribute is added to the
component parts.

```css
[data-part="root"][data-disabled] {
  /* styles for when the file upload is disabled */
}

[data-part="dropzone"][data-disabled] {
  /* styles for when the file upload is disabled */
}

[data-part="trigger"][data-disabled] {
  /* styles for when the file upload is disabled */
}

[data-part="label"][data-disabled] {
  /* styles for when the file upload is disabled */
}
```

## Methods and Properties

### Machine Context

The file upload machine exposes the following context properties:

<ContextTable name="file-upload" />

### Machine API

The file upload `api` exposes the following methods:

<ApiTable name="file-upload" />

### Data Attributes

<DataAttrTable name="file-upload" />
